import { isFunction } from '@vue/shared'
import useUnMounted from "./useUnMounted"
import useMounted from './useMounted'
import useEventListener from "./useEventListener"
import { toRefs, useMutationObserver } from '@vueuse/core'
import { throttle } from 'lodash-unified'

const dragging = (target, options = {}) => {
    var _a, _b;
    const {
        pointerTypes,
        preventDefault,
        stopPropagation,
        exact,
        onMove,
        onEnd,
        onStart,
        initialValue,
        autoStyle = true,
        axis = "both",
        storageKey,
        moveCursor = false,
        storageType = localStorage,
        draggingElement = window,
        container = document.body,
        handle: draggingHandle = target
    } = options;
    let onselectstart = null
    if (moveCursor) toValue(draggingHandle).classList.add("drag-handle")
    const getStorageValue = () => {
        try {
            return JSON.parse(storageType?.getItem(storageKey))
        } catch (e) {
            return null
        }
    }
    const initialPosition = (storageKey ? getStorageValue() : null) || { x: 0, y: 0 };
    const reset = toValue(initialValue) || storageKey
    const targetElement = toValue(target)
    let cssText = targetElement.style.cssText
    const position = ref((_a = toValue(initialValue)) != null ? _a : initialPosition);
    if (reset && autoStyle) targetElement.style.cssText = `${cssText};transform:translate(${position.value.x}px,${position.value.y}px)`
    const pressedDelta = ref();
    const filterEvent = (e) => {
        if (pointerTypes) return pointerTypes.includes(e.pointerType);
        return true;
    };
    const handleEvent = (e) => {
        if (toValue(preventDefault)) e.preventDefault();
        if (toValue(stopPropagation)) e.stopPropagation();
    };
    const targetOffset = ref({ x: 0, y: 0 })
    const start = (e) => {
        cssText = targetElement.style.cssText
        onselectstart = document.body.onselectstart
        document.body.onselectstart = () => false; // 阻止选中文本
        var _a2;
        if (e.button !== 0) return;
        if (toValue(options.disabled) || !filterEvent(e)) return;
        if (toValue(exact) && e.target !== targetElement) return;
        if (moveCursor) document.documentElement.classList.add("dragging")
        const containerElement = toValue(container);
        targetOffset.value.x = targetElement.offsetLeft
        targetOffset.value.y = targetElement.offsetTop
        const containerRect = (_a2 = containerElement == null ? void 0 : containerElement.getBoundingClientRect) == null ? void 0 : _a2.call(containerElement);
        const targetRect = targetElement.getBoundingClientRect();
        const pos = {
            x: e.clientX - (containerElement ? targetRect.left - containerRect.left + containerElement.scrollLeft : targetRect.left) + targetOffset.value.x,
            y: e.clientY - (containerElement ? targetRect.top - containerRect.top + containerElement.scrollTop : targetRect.top) + targetOffset.value.y
        };
        if ((onStart == null ? void 0 : onStart(pos, e)) === false) return;
        pressedDelta.value = pos;
        handleEvent(e);
    };

    const move = throttle((e) => {
        if (toValue(options.disabled) || !filterEvent(e)) return;
        if (!pressedDelta.value) return;
        const containerElement = toValue(container);
        const targetRect = targetElement.getBoundingClientRect();
        let { x, y } = position.value;
        if (axis === "x" || axis === "both") {
            x = e.clientX - pressedDelta.value.x;
            if (containerElement) x = Math.min(Math.max(-1 * targetOffset.value.x, x), containerElement.scrollWidth - targetRect.width - targetOffset.value.x);
        }
        if (axis === "y" || axis === "both") {
            y = e.clientY - pressedDelta.value.y;
            if (containerElement) y = Math.min(Math.max(-1 * targetOffset.value.y, y), containerElement.scrollHeight - targetRect.height - targetOffset.value.y);
        }

        position.value = { x, y };
        onMove == null ? void 0 : onMove(position.value, e);
        handleEvent(e);
        if (autoStyle) targetElement.style.cssText = `${cssText};transform:translate(${position.value.x}px,${position.value.y}px) !important`
    });
    const end = (e) => {
        if (toValue(options.disabled) || !filterEvent(e)) return;
        if (!pressedDelta.value) return;
        document.documentElement.classList.remove("dragging")
        pressedDelta.value = void 0;
        onEnd == null ? void 0 : onEnd(position.value, e);
        handleEvent(e);
        if (storageKey && !toValue(initialValue)) {
            storageType?.setItem(storageKey, JSON.stringify(position.value))
        }
        document.body.onselectstart = onselectstart; // 阻止选中文本
    };
    const config = { capture: (_b = options.capture) != null ? _b : true };
    const stopWatchPointerdown = useEventListener(draggingHandle, "pointerdown", start, config);
    const stopWatchPointermoven = useEventListener(draggingElement, "pointermove", move, config);
    const stopWatchPointerup = useEventListener(draggingElement, "pointerup", end, config);
    const stop = () => {
        stopWatchPointerdown();
        stopWatchPointermoven();
        stopWatchPointerup();
    };
    return {
        ...toRefs(position),
        position,
        isDragging: computed(() => !!pressedDelta.value),
        ...toRefs(computed(() => { return { lx: `${position.value.x}px`, ly: `${position.value.y}px` } })),
        style: computed(() => `transform: translate(${position.value.x}px,${position.value.y}px)`),
        stop
    };
}

export const useObserverElement = (selector) => {
    const elementExist = ref(false)
    const observer = useMutationObserver(document, () => {
        if (document.querySelector(selector)) {
            elementExist.value = true
        } else {
            elementExist.value = false
        }
    }, {
        childList: true,
        subtree: true
    })
    return { exist: elementExist, stop: observer.stop }
}

export default (selector, options = {}) => {
    var stopDrag = () => void 0
    var stopObserver = () => void 0
    const { targetExist } = options
    const fromOptionGetExist = isRef(targetExist) || isReactive(targetExist) || isFunction(targetExist)
    let divExist = isFunction(targetExist) ? computed(targetExist) : ref(targetExist)
    const start = () => {
        if (typeof selector === 'string') {
            if (!fromOptionGetExist) {
                const { exist, stop } = useObserverElement(selector)
                console.warn(`useDraggable正在使用useObserverElement监听${selector}元素`)
                divExist = exist
                stopObserver = stop
            }
            const handle = options.handle
            let dragResult = ref({})
            watch(divExist, (exist) => {
                if (exist) {
                    nextTick(() => {
                        const hl = typeof handle === 'string' ? document.querySelector(handle) : handle
                        const target = document.querySelector(selector)
                        if (!target) return
                        dragResult.value = dragging(target, { ...options, handle: hl })
                        const { stop } = dragResult.value
                        stopDrag = stop
                    })
                } else {
                    stopDrag()
                }
            }, { immediate: true })
            return dragResult
        } else {
            return dragging(selector, options)
        }
    }
    useUnMounted(() => {
        stopDrag()
        stopObserver()
    })

    return useMounted(start)
}